const mongoose = require('mongoose')
const bcrypt = require('bcrypt')

const Schema = mongoose.Schema
const Mixed = Schema.Types.Mixed

const SALT_WORK_FACTOR = 10
const MAX_LOGIN_ATTEMPTS = 5
const LOCK_TIME = 2 * 60 * 60 * 1000

const userSchema = new Schema({
  username: {
    unique: true,
    required: true,
    type: String
  },
  email: {
    unique: true,
    required: true,
    type: String
  },
  role: {
    type: String,
    default: 'user'
  },
  password: {
    unique: true,
    required: true,
    type: String
  },
  loginAttempts: {
    type: Number,
    required: true,
    default: 0
  },
  lockUntil: Number,
  meta: {
    createdAt: {
      type: Date,
      default: Date.now()
    },
    updatedAt: {
      type: Date,
      default: Date.now()
    }
  }
})
/*
// 虚拟字段
// UserSchema.virtual
UserSchema.virtual('isLocked').get( function() {
  return !!(this.lockUntil && this.lockUntil > Date.now())
})

// 更新时间
UserSchema.pre('save', function (next) {
  if (this.isNew) this.meta.createdAt = this.meta.updatedAt = Date.now()
  else this.meta.updatedAt = Date.now()
  
  next()  
})

// 密码明文 问题
UserSchema.pre('save', function (next) {

  // 检测 密码变更
  if (!this.isModified('possword')) return next()

  // 参数一个 salt
  bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
    if (err) return next(err)

    // 结合 salt 产生出新的 hash
    bcrypt.hash(this.password, salt, (error, hash) => {
      if (err) return next(err)

      // 使用 hash 覆盖明文密码
      this.password = hash
      next()
    }) 
    
  })
})
*/

userSchema.virtual('isLocked').get(function () {
  return !!(this.lockUntil && this.lockUntil > Date.now())
})

userSchema.pre('save', function (next) {
  if (this.isNew) {
    this.meta.createdAt = this.meta.updatedAt = Date.now()
  } else {
    this.meta.updatedAt = Date.now()
  }

  next()
})

userSchema.pre('save', function (next) {
  if (!this.isModified('password')) return next()

  bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
    if (err) return next(err)

    bcrypt.hash(this.password, salt, (error, hash) => {
      if (error) return next(error)

      this.password = hash
      next()
    })
  })
})


userSchema.methods = {
  comparePassword: (_password, password) => {
    return new Promise((resolve, reject) => {
      bcrypt.compare(_password, password, (err, isMatch) => {
        if (err) {
          console.log(err, '...')
          reject(err)
        } else {
          console.log(isMatch, '...')
          resolve(isMatch)
        }
      })
    })
  },

  incLoginAttempts: (user) => { // 密码错误，登录次数限制
    return new Promise((resolve, reject) => {
      if (this.lockUntil && this.lockUntil < Date.now()) { // 被锁定
        this.updaye({
          $set: { loginAttempts: 1 },
          $unset: { lockUntil: 1}
        }, err => {
          if (err) reject(err)
          else resolve(true)  
        })
      } else { // 未被锁定
        let updates = { $inc: { loginAttempts: 1}} // 累加

        if (this.loginAttempts + 1 >= MAX_LOGIN_ATTEMPTS && !this.isLocked) { // 边界值
          updates.$set = { lockUntil: Date.now() + LOCK_TIME }
        }

        this.update(updates, err => {
          if (err) reject(err)
          else resolve(true)
        })
      }
    })
  }
}

mongoose.model('User', userSchema)